package model.framework;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import model.config.Config;

public class Field {
	private char[][] field;
	private int blockCounter;
	private List<Block> blockList;
	//private String possibleCharacters = "#@$%&}{][";
	//private String possibleCharacters = "123456789";
	private String possibleCharacters = "abcdefghijklmnopqrstuvwxyz";
	private int fieldWidth;
	
	private final char emptyChar = '.';
	/**
	 * Constructor, properties are found in the Config.java file
	 */
	public Field(int width) {
		this.fieldWidth = width;
		field			= new char[ width ][ 1 ];
		for (char[] chars: field){
			for (int i = 0; i < chars.length; i++){
				chars[i] = emptyChar;
			}
		}
		blockCounter	= 0;
		blockList		= new ArrayList<Block>();
		
		//Config.p( "Field created: ["+ Config.FIELD_WIDTH +"]["+ Config.FIELD_HEIGTH +"]" );
	}
	
	/**
	 * Places the given blockProperties as key and block as value
	 * @param x
	 * @param y
	 * @param rotation
	 * @param block
	 */
	public void placeBlock( Block block ) {
		/* Current field height */
		int currentFieldHeight = field[0].length;
		
		/* DIT LATER AANPASSEN? */
		block.setY(block.getY() + getHeightBelowSurface());
		
		/* Does the Block fit in the current field? */
		if( ( block.getY() + block.getHeight() ) > currentFieldHeight ) {
			
			/* Add extra rows */
			addRowsToField( ( block.getY() + block.getHeight() ) - currentFieldHeight );
		}
		
		/* How does the current Block look? */
		int[][] blockDescription = block.getDescription();
		
		/* Loop through the description */
		for( int indexX = 0; indexX < blockDescription.length; indexX++ ) {
			for( int indexY = 0; indexY < blockDescription[ indexX ].length; indexY++ ) {
				
				/* Set the occupied spaces */
				if( blockDescription[ indexX ][ indexY ] == 1 ) {
					if (field[ block.getX() + indexX ][ block.getY() + indexY ] != emptyChar){
						throw new RuntimeException("Place was already taken, illegal placeblock call ("+block.getX()+","+block.getY()+","+block.getRotation()+")");
					}
					field[ block.getX() + indexX ][ block.getY() + indexY ] = possibleCharacters.charAt(blockCounter % possibleCharacters.length());
				//	field[ x + indexX ][ y + indexY ] = Character.forDigit(rotation,10);
				}
			}
		}

		/* Some administration */
		
		/* File it */
		blockList.add( block );
		
		/* And another one bites the dust */
		blockCounter++;
	}
	
	/**
	 * Adds given rows to the current field
	 * @param rows
	 */
	private void addRowsToField( int rows ) {
		/* Create an even bigger field! */
		char[][] newField = new char[ field.length ][ field[0].length + rows ]; 
		
		/* Copy each and every value */
		for( int indexX = 0; indexX < field.length; indexX++ ) {
			for( int indexY = 0; indexY < field[ indexX ].length; indexY++ ) {
				newField[ indexX ][ indexY ] = field[ indexX ][ indexY ];
			}
			for (int extra = field[indexX].length; extra < newField[indexX].length; extra++){
				newField[indexX][extra] = emptyChar;
			}
		}
		
		/* And replace! */
		field = newField;
	}
	
	/**
	 * Places the given blockProperties as key and block as value
	 * @param blockProperties
	 * @param block
	 */
	public void placeBlock( Block blockProperties, BlockType block ) {
		blockList.add( blockProperties );
	}
	
	/**
	 * Returns the current surface
	 * @return surface
	 */
	public int[] getSurface() {
		/* Get the full surface */
		int[] surface = calculateSurface();
		
		/* So we don't have any overhead in the return array */
		int lowest = calculateLowestSurface( surface );
		
		/* Subtract the lowest value from all the surfaces so we won't have any unnecessary data */
		for( int index = 0; index < surface.length; index++ ) {
			surface[ index ] = surface[ index ] - lowest;
		}
		
		/* There ya go */
		return surface;
	}
	
	/**
	 * Returns the surface including the blocks that aren't used
	 * @return surface
	 */
	private int[] calculateSurface() {
		/* So we have something to fill in */
		int[] surface = new int[ field.length ];
		
		/* Loop through it left to right */
		for( int indexX = 0; indexX < field.length; indexX++ ) {
			
			/* Loop through it bottom to top */
			for( int indexY = 0; indexY < field[ indexX ].length; indexY++ ) {
				
				/* There is a ( piece of ) block here! Better make a note of it */
				//if( possibleCharacters.contains(""+field[ indexX ][ indexY ])) {
				if( emptyChar != field[ indexX ][ indexY ]) {

					/* Remember the current yPosition */
					surface[ indexX ] = indexY + 1;
				}
			}
		}
		
		/* There ya go */
		return surface;
	}
	
	/**
	 * Finds the lowest surface height in the given surface
	 * @param surface
	 * @return lowest value from surface
	 */
	private int calculateLowestSurface( int[] surface ) {
		/* So we don't have any overhead in the return array */
		int lowest = field[0].length;
		
		/* Find the lowest surface value */
		for( int index = 0; index < surface.length; index++ ) {
			if( surface[ index ] < lowest ) {
				lowest = surface[ index ];
			}
		}
		
		return lowest;
	}
	
	/**
	 * Get the current field height, below the surface
	 * @return the current field height
	 */
	public int getHeightBelowSurface() {
		return calculateLowestSurface( calculateSurface() );
	}
	
	/**
	 * Returns a string representation from the field
	 */
	public String toString() {
		/* The return String */
		String fieldString = "";
		
		/* We're going to loop through the field from the top to the bottom */
		for( int indexY = field[0].length - 1; indexY >= 0; indexY-- ) {
			
			/* Loop left to right */
			for( int indexX = 0; indexX < field.length; indexX++ ) {
				fieldString += ""+ field[ indexX ][ indexY ];
			}
			
			/* Lines are made to be broken.. */
			fieldString += "\n";
		}
		
		/* There ya go */
		return fieldString;
	}
	
	/**
	 * Prints the current field to the terminal
	 */
	public void printField() {
		Config.p( toString() );
	}
	
	public Field clone(){
		Field newField = new Field(this.fieldWidth);
		newField.field = new char[field.length][field[0].length];
		for (int i  = 0; i < field.length; i++){
			newField.field[i] = field[i].clone();
		}
		newField.blockCounter = this.blockCounter;
		List<Block> newBlockTable = new ArrayList<Block>();
		newBlockTable.addAll(blockList);
		newField.blockList = newBlockTable;
		return newField;
		
	}
	
	public int getWidth(){
		return field.length;
	}
	
	public int getTotalHeight(){
		return field[0].length;
	}
	
	public List<Block> getBlockList(){
		return this.blockList;
	}
}